package cn.spdb.harrier.rpc.client;

import java.lang.reflect.Method;

import org.apache.commons.lang3.ObjectUtils;

import com.alibaba.fastjson.JSON;

import cn.spdb.harrier.common.utils.URI;
import cn.spdb.harrier.rpc.common.InterfaceRpcCallBack;
import cn.spdb.harrier.rpc.common.RpcHandler;
import cn.spdb.harrier.rpc.common.RpcMethod;
import cn.spdb.harrier.rpc.common.RpcRequest;
import cn.spdb.harrier.rpc.common.RpcResponse;
import cn.spdb.harrier.rpc.protocol.RpcProtocol;
import cn.spdb.harrier.rpc.remote.NettyClient;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;

public class ConsumerInterceptor {

	private URI uri;
	private NettyClient nettClient = NettyClient.getInstance();

	public ConsumerInterceptor(URI uri) {
		this.uri = uri;
	}

/**
 * 拦截方法调用，构建RPC请求并发送到远程服务端，处理调用过程及重试机制。
 *
 * @param args  方法调用的参数数组
 * @param method 被拦截的方法对象
 * @return 返回远程调用的结果对象或异常
 */
@RuntimeType
public Object intercept(@AllArguments Object[] args, @Origin Method method) {
    // 构建RPC请求对象
    RpcRequest request = buildReq(args, method);

    // 获取处理方法名称（用于配置和服务发现）
    String handlerMethName = request.getHandlerMethName();

    // 从缓存中获取消费者配置
    ConsumerConfig consumerConfig = ConsumerConfigCache.getConfigByServersName(handlerMethName);

    // 如果缓存中没有配置，则根据方法注解构建并缓存配置
    if (null == consumerConfig) {
        consumerConfig = cacheServiceConfig(method, handlerMethName);
    }

    // 获取最大重试次数
    int retries = consumerConfig.getRetries();

    // 获取方法返回类型，用于后续结果转换
    Class<?> returnType = method.getReturnType();

    // 构建带有压缩类型的RPC协议对象
    RpcProtocol<RpcRequest> protocol = RpcRequest.buildProtocol(request, consumerConfig.getCompressType());

    // 执行远程调用，并根据重试策略进行重试
    while (retries-- > 0) {
        RpcResponse rsp;

        // 发送RPC请求并接收响应
        rsp = nettClient.sendMsg(uri, protocol, consumerConfig);

        // 判断响应是否成功
        if (null != rsp && rsp.getStatus() == 0) {
            // 获取响应结果
            Object object = rsp.getResult();

            // 如果结果是JSON类型，将其转换为对应的Java对象
            if (JSON.class.isInstance(object)) {
                object = JSON.toJavaObject((JSON) object, returnType);
            }

            // 返回最终结果
            return object;
        }
    }

    // 如果所有重试都失败，返回异常结果
    return new Exception("send msg error");
}


/**
 * 构建RPC请求对象，封装调用的方法信息和参数
 *
 * @param args   方法调用的参数数组
 * @param method 被拦截的方法对象
 * @return 返回封装好的 RpcRequest 请求对象
 */
private RpcRequest buildReq(Object[] args, Method method) {
    // 创建一个新的RpcRequest对象
    RpcRequest request = new RpcRequest();

    // 获取方法所属类上的 @RpcHandler 注解
    RpcHandler rpcHandler = method.getDeclaringClass().getAnnotation(RpcHandler.class);

    // 如果注解存在并且设置了value值，则使用该值作为服务标识；否则使用类名
    String className = (ObjectUtils.isEmpty(rpcHandler) || ObjectUtils.isEmpty(rpcHandler.value()))
            ? method.getDeclaringClass().getSimpleName()
            : rpcHandler.value();

    // 设置请求中的类名（服务标识）
    request.setClassName(className);

    // 设置请求的方法名
    request.setMethodName(method.getName());

    // 设置方法的参数类型数组
    request.setParameterTypes(method.getParameterTypes());

    // 设置方法调用的实际参数值
    request.setParameters(args);

    // 返回构建完成的请求对象
    return request;
}


/**
 * 根据方法上的 {@link RpcMethod} 注解构建并缓存服务调用配置
 *
 * @param method         被拦截的方法对象
 * @param handlerMethName 方法的唯一标识名称
 * @return 返回构建好的 ConsumerConfig 配置对象
 */
private ConsumerConfig cacheServiceConfig(Method method, String handlerMethName) {
    // 创建一个新的消费者配置对象
    ConsumerConfig consumerConfig = new ConsumerConfig();

    // 设置方法名用于后续服务匹配和配置查找
    consumerConfig.setHandlerMethName(handlerMethName);

    // 检查该方法是否被 @RpcMethod 注解修饰
    boolean annotationPresent = method.isAnnotationPresent(RpcMethod.class);
    if (annotationPresent) {
        // 获取注解实例
        RpcMethod rpcMethod = method.getAnnotation(RpcMethod.class);

        // 从注解中提取配置信息，设置到 consumerConfig 中
        consumerConfig.setAsync(rpcMethod.async());
        consumerConfig.setTimeOut(rpcMethod.timeOut());
        consumerConfig.setSeriveCallBackClass(rpcMethod.serviceCallBack());

        // 如果回调类实现了 InterfaceRpcCallBack 接口，则标记为需要回调
        if (InterfaceRpcCallBack.class.isAssignableFrom(rpcMethod.serviceCallBack())) {
            consumerConfig.setCallBack(true);
        }

        // 设置重试次数和压缩类型
        consumerConfig.setRetries(rpcMethod.retries());
        consumerConfig.setCompressType(rpcMethod.compressType().getType());
    }

    // 将配置缓存起来，供后续调用复用
    ConsumerConfigCache.putConfig(handlerMethName, consumerConfig);

    // 返回构建完成的配置对象
    return consumerConfig;
}


}
